/*
 * Copyright 2010 Biz-e (info@biz-e.nl)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.biz_e.gwt.baselib.ui.client.widgets;

import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DeckPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.RichTextArea;
import com.google.gwt.user.client.ui.TextArea;

/**
 * The RichTextEditor encapsulates the GWT default {@link RichTextArea} and adds
 * a toolbar for easy editting. The Toolbar is heavily based on
 * the toolbar provided by the GWT examples. Mostly I added functionality to
 * disable the buttons and a new button that switches to HTML source editing
 * mode.
 * 
 * @author Joost A. Bloemsma
 */
public class RichTextEditor extends Composite {
    /**
     * Enumeration of the two different states the editor can be in
     */
    public static enum State {
        /**
         * The editor is in WYSIWYG editing state
         */
        RICH,
        /**
         * The editor is in HTML source editing state
         */
        SOURCE
    }

    private final RichTextArea area;
    private final TextArea source;
    private final DeckPanel deck;
    private final Grid grid;
    private final RichTextToolbar toolbar;
    private State state = State.RICH;
    private boolean resizeable = true;

    /**
     * Constructs a new RichTextEditor instance
     */
    public RichTextEditor() {
        grid = new Grid(2, 1);
        this.initWidget(grid);
        grid.setWidth("100%");
        area = new RichTextArea();
        source = new TextArea();
        toolbar = new RichTextToolbar(this);
        // TODO: temporary, while toolbar doesn't support autolayout
        toolbar.setWidth("429px");
        toolbar.addStyleName("fullSoftBorder");
        // Add the components to a panel
        this.setStyleName("noBorder");
        area.addStyleName("noBorder");
        source.addStyleName("fullSoftBorder");
        grid.setWidget(0, 0, toolbar);
        deck = new DeckPanel();
        deck.add(area);
        deck.setAnimationEnabled(false);
        deck.add(source);
        deck.addStyleName("withRichTextToolbar");
        grid.setWidget(1, 0, deck);
        deck.setSize("100%", "20em");
        // deck.setSize("100%", "100%");
        this.setState(State.RICH);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onAttach() {
        super.onAttach();
        grid.getCellFormatter().setHeight(0, 0,
                toolbar.getOffsetHeight() + "px");
        area.setSize("100%", "100%");
        source.setSize("100%", "100%");
        Window.addResizeHandler(new ResizeHandler() {

            @Override
            public void onResize(ResizeEvent event) {
                if (resizeable)
                    RichTextEditor.this.onResize();
            }
        });
        this.onResize();
    }

    /**
     * @return A reference to the richtext area
     */
    public RichTextArea getRichTextArea() {
        return this.area;
    }

    /**
     * @return the area
     */
    RichTextArea getArea() {
        return area;
    }

    /**
     * @return the source
     */
    TextArea getSource() {
        return source;
    }

    /**
     * @return the deck
     */
    DeckPanel getDeck() {
        return deck;
    }

    /**
     * @return The current {@link State} the editor is in
     */
    public State getState() {
        return this.state;
    }

    /**
     * Switches the {@link State} of the editor
     * 
     * @param state
     */
    public void setState(State state) {
        // First, copy the new content to the other state
        String html = this.getHTML();
        this.state = state;
        this.setHTML(html);
        if (state == State.RICH) {
            deck.showWidget(0);
        } else {
            deck.showWidget(1);
        }
    }

    /**
     * Returns the HTML source for the current content.
     * 
     * @return the HTML source
     */
    public String getHTML() {
        switch (state) {
            case RICH:
                return area.getHTML();
            case SOURCE:
                return source.getText();
            default:
                return "";

        }
    }

    /**
     * Sets any arbitrary HTML.<br/>
     * See also {@link RichTextArea#setHTML(String)}
     * 
     * @param html
     */
    public void setHTML(String html) {
        switch (state) {
            case RICH:
                area.setHTML(html);
            case SOURCE:
                source.setText(html);
        }
    }

    /**
     * @return the resizeable
     */
    public boolean isResizeable() {
        return resizeable;
    }

    /**
     * @param resizeable
     *            the resizeable to set
     */
    public void setResizeable(boolean resizeable) {
        this.resizeable = resizeable;
    }

    /**
     * Implement resize logic in subclass
     */
    protected void onResize() {
        return;
    }

    /**
     * calls {@link DeckPanel#getAbsoluteTop()} on the deck component
     * 
     * @return the deck's absolute top position
     */
    protected int getDeckTop() {
        return deck.getAbsoluteTop();
    }

    /**
     * Sets the height of the deck in pixels
     * 
     * @param deckHeight
     */
    protected void setDeckHeight(int deckHeight) {
        deck.setHeight(Integer.toString(deckHeight) + "px");
    }
}
